package org.artifact.builder;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.artifact.builder.design.DesignConfig;
import org.artifact.builder.design.DesignDatabase;
import org.artifact.builder.design.DesignTable;
import org.artifact.builder.template.DesignTemplate;
import org.artifact.core.exception.ArtifactExceptionUtil;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateConfig.ResourceMode;
import cn.hutool.extra.template.TemplateEngine;
import cn.hutool.extra.template.TemplateUtil;
import lombok.Getter;

/**
 * @author : DengYing
 * @CreateDate : 2018年2月24日 下午6:52:30
 * @Description ：模块生成器抽象
 */
@Getter
public abstract class AbstractModeleBuilder {
	/** 模板组 */
	private TemplateConfig templateConfig;

	/** 生成的源码路径 */
	private String sourcePath;

	/** 生成源码的包名称 */
	private String packageName;

	/** 设计文件路径 */
	private String designSourcePath;
	
	private DesignConfig designConfig;

	// public static DBModulebuilder newBuilder() {
	// DBModulebuilder builder = new DBModulebuilder();
	// return builder;
	// }

	public void build() throws Exception {
		if (this.templateConfig == null) {
			useDefaultGroupTemplate();
		}
		
		// 检查参数
		ArtifactExceptionUtil.isTrue(0, "没有指定生成的-配置参数", designConfig == null);
		
		// 检查参数
		ArtifactExceptionUtil.isTrue(0, "没有指定生成的-源码路径", StrUtil.isBlank(sourcePath));

		ArtifactExceptionUtil.isTrue(0, "没有指定生成的-包名称", StrUtil.isBlank(packageName));

		ArtifactExceptionUtil.isTrue(0, "没有指定生成的-源文件路径", designSourcePath == null);

		check();

		verify();

		execute(new DesignDatabase(designConfig,designSourcePath));
		
		completeInfo();
	}

	/**
	 * 使用默认模板组
	 * 
	 * @return
	 */
	public void useDefaultGroupTemplate() {
		this.templateConfig = new TemplateConfig();
		this.templateConfig.setCharset(CharsetUtil.CHARSET_UTF_8);
		this.templateConfig.setResourceMode(ResourceMode.CLASSPATH);
	}

	/**
	 * 成功打印信息
	 */
	public static void completeInfo() {
		System.out.println("*********************************************");
		System.out.println("*                                           *");
		System.out.println("**********代码生成完成-请刷新项目！**********");
		System.out.println("*                                           *");
		System.out.println("*********************************************");
		System.exit(0);
	}
	// --------------------------------- abstract method
	// ---------------------------------

	public abstract Map<String,DesignTemplate> getDesignTemplate();

	/** 执行检查 */
	public void check() {
		getDesignTemplate().forEach((k,v)->{
			v.mkdir(getSourcePath());
		});
	}

	/** 执行验证 */
	public void verify() {

	}

	/**
	 * 执行生成
	 * 
	 * @throws IOException
	 */
	public void execute(DesignDatabase designDatabase) throws IOException {
		TemplateEngine engine = TemplateUtil.createEngine(this.templateConfig);
		
		Collection<DesignTable> tables = designDatabase.getTables().values();
		// 并行生成各个表
		tables.parallelStream().forEach(designTable -> {
			// 设置模板
			Map<String, DesignTemplate> templates = getDesignTemplate();
			
			Map<String, Object> bindingMap = new HashMap<>();
			bindingMap.put("database", designDatabase);
			bindingMap.put("table", designTable);
			bindingMap.put("templates", templates);
			
			for (Entry<String, DesignTemplate> entry : templates.entrySet()) {
				DesignTemplate designTemplate = entry.getValue();
				// 取得模板
				Template template = engine.getTemplate(designTemplate.getTemplateName());
				
				String pathname = designTemplate.getPath() + File.separator + designTable.getClassName() + designTemplate.getClassSuffix() + designTemplate.getFileSuffix();
				
				FileWriter fileWriter;
				try {
					fileWriter = new FileWriter(new File(pathname));
					template.render(bindingMap, fileWriter);
					fileWriter.flush();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});
	}
	// --------------------------------- Get & Set ---------------------------------
	public AbstractModeleBuilder setTemplateConfig(TemplateConfig templateConfig) {
		this.templateConfig = templateConfig;
		return this;
	}

	public AbstractModeleBuilder setSourcePath(String sourcePath) {
		this.sourcePath = sourcePath;
		return this;
	}

	public AbstractModeleBuilder setPackageName(String packageName) {
		this.packageName = packageName;
		return this;
	}

	public AbstractModeleBuilder setDesignSourcePath(String designSourcePath) {
		this.designSourcePath = designSourcePath;
		return this;
	}

	public AbstractModeleBuilder setDesignConfig(DesignConfig designConfig) {
		this.designConfig = designConfig;
		return this;
	}
	
}
